
#include <FS.h> //闪存文件系统

#if defined (ESP8266)
#include <ESP8266WiFi.h>
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#endif

#if defined (ESP32)
#include <WiFi.h>
#include <DNSServer.h>
#include <WebServer.h>
#endif

#include <ArduinoJson.h>

//*********************web配网*******************//
const char* AP_NAME = "墨水屏台历";//wifi名字
//暂时存储wifi账号密码和uid
char ssid[32] = {0};
char password[64] = {0};
//String scanNetworksID = "";
bool needsave = 0;

//配网页面代码
const char* page_html = "\
<!DOCTYPE html>\r\n\
<html lang='en'>\r\n\
<head>\r\n\
<meta charset='UTF-8'>\r\n\
<meta name='viewport' content='width=device-width, initial-scale=1.0'>\r\n\
<title>Document</title>\r\n\
</head>\r\n\
<body>\r\n\
<form name='input' action='/' method='POST'>\r\n\
      wifi名称: <br>\r\n\
      <input type='text' name='ssid'><br>\r\n\
      wifi密码:<br>\r\n\
      <input type='text' name='password'><br>\r\n\
      citycode:<br>\r\n\
    <input type='text' name='city'><br>\r\n\
      <input type='submit' value='保存'>\r\n\
  </form>\r\n\
</body>\r\n\
</html>\r\n\
";


// 上下两段HTML代码
//const char* ROOT_HTML_1 = "<!DOCTYPE html><html><head>  <meta charset=\"UTF-8\">  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">  <meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\" />  <title>登录页面</title>  <style>   #content,.login,.login-card a,.login-card h1,.login-help{text-align:center}body,html{margin:0;padding:0;width:100%;height:100%;display:table}#content{font-family:\'Source Sans Pro\',sans-serif;-webkit-background-size:cover;-moz-background-size:cover;-o-background-size:cover;background-size:cover;display:table-cell;vertical-align:middle}.login-card{padding:40px;width:274px;background-color:#F7F7F7;margin:0 auto 10px;border-radius:20px;box-shadow:8px 8px 15px rgba(0,0,0,.3);overflow:hidden}.login-card h1{font-weight:400;font-size:2.3em;color:#1383c6}.login-card h1 span{color:#f26721}.login-card img{width:70%;height:70%}.login-card input[type=submit]{width:100%;display:block;margin-bottom:10px;position:relative}.login-card input[type=text],input[type=password]{height:44px;font-size:16px;width:100%;margin-bottom:10px;-webkit-appearance:none;background:#fff;border:1px solid #d9d9d9;border-top:1px solid silver;padding:0 8px;box-sizing:border-box;-moz-box-sizing:border-box}.login-card input[type=text]:hover,input[type=password]:hover{border:1px solid #b9b9b9;border-top:1px solid #a0a0a0;-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.login{font-size:14px;font-family:Arial,sans-serif;font-weight:700;height:36px;padding:0 8px}.login-submit{-webkit-appearance:none;-moz-appearance:none;appearance:none;border:0;color:#fff;text-shadow:0 1px rgba(0,0,0,.1);background-color:#4d90fe}.login-submit:disabled{opacity:.6}.login-submit:hover{border:0;text-shadow:0 1px rgba(0,0,0,.3);background-color:#357ae8}.login-card a{text-decoration:none;color:#666;font-weight:400;display:inline-block;opacity:.6;transition:opacity ease .5s}.login-card a:hover{opacity:1}.login-help{width:100%;font-size:12px}.list{list-style-type:none;padding:0}.list__item{margin:0 0 .7rem;padding:0}label{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;text-align:left;font-size:14px;}input[type=checkbox]{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;margin-right:10px;float:left}.error{font-size:14px;font-family:Arial,sans-serif;font-weight:700;height:25px;padding:0 8px;padding-top: 10px; -webkit-appearance:none;-moz-appearance:none;appearance:none;border:0;color:#fff;text-shadow:0 1px rgba(0,0,0,.1);background-color:#ff1215}@media screen and (max-width:450px){.login-card{width:70%!important}.login-card img{width:30%;height:30%}}  </style></head><body style=\"background-color: #e5e9f2\"><div id=\"content\"> <form name=\'input\' action=\'/configwifi\' method=\'POST\'>  <div class=\"login-card\">    <h1>WiFi登录</h1>   <form name=\"login_form\" method=\"post\" action=\"$PORTAL_ACTION$\">   <input type=\"text\" name=\"ssid\" placeholder=\"请输入 WiFi 名称\" id=\"auth_user\" list = \"data-list\"; style=\"border-radius: 10px\">    <datalist id = \"data-list\">";
//const char* ROOT_HTML_2 = "<input type=\"password\" name=\"password\" placeholder=\"请输入 WiFi 密码\" id=\"auth_pass\"; style=\"border-radius: 10px\">      <div class=\"login-help\">        <ul class=\"list\">          <li class=\"list__item\">          </li>        </ul>      </div>   <input type=\"submit\" class=\"login login-submit\" value=\"确 定 连 接\" id=\"login\"; disabled; style=\"border-radius: 15px\"  >    </form> <!-- <form name=\'input\' action=\'/English\' method=\'POST\'>    <input type=\"submit\" class=\"login login-submit\" value=\"English\" id=\"login\"; disabled; style=\"border-radius: 15px\"  >    </form> --></body></html>";


const byte DNS_PORT = 53;//DNS端口号
IPAddress apIP(192, 168, 4, 1);//esp8266-AP-IP地址
DNSServer dnsServer;//创建dnsServer实例

#if defined (ESP8266)
ESP8266WebServer server(80);//创建WebServer
#endif

#if defined (ESP32)
WebServer server(80);//创建WebServer
#endif


void handleRoot();
void handleConfigWifi();
bool scanWiFi();
void initBasic();
void initSoftAP();
void initWebServer();
void initDNS();
void wifiConfig();
void removeConfig();
bool WIFI_init();

/*
   处理网站根目录的访问请求
*/
//void handleRoot()
//{
//  if (server.hasArg("selectSSID")) {
//    server.send(200, "text/html", ROOT_HTML_1 + scanNetworksID + ROOT_HTML_2);   //scanNetWprksID是扫描到的wifi
//  } else {
//    server.send(200, "text/html", ROOT_HTML_1 + scanNetworksID + ROOT_HTML_2);
//  }
//}
void handleRoot() {//访问主页回调函数
  server.send(200, "text/html", page_html);
}

/*
   提交数据后的提示页面
*/
void handleConfigWifi()               //返回http状态
{
  if (server.hasArg("ssid"))          //判断是否有账号参数
  {
    Serial.print("got ssid:");
    strcpy(ssid, server.arg("ssid").c_str());   //获取html表单输入框name名为"ssid"的内容
    Serial.println(ssid);
  }
  else                                //没有参数
  {
    Serial.println("error, not found ssid");
    server.send(200, "text/html", "<meta charset='UTF-8'>error, not found ssid"); //返回错误页面
    return;
  }
  //密码与账号同理
  if (server.hasArg("password"))
  {
    Serial.print("got password:");
    strcpy(password, server.arg("password").c_str());  //获取html表单输入框name名为"pwd"的内容
    Serial.println(password);
  }
  else
  {
    Serial.println("error, not found password");
    server.send(200, "text/html", "<meta charset='UTF-8'>error, not found password");
    return;
  }
  if (server.hasArg("city"))
  {
    Serial.print("got citycode:");
    citycode = server.arg("city").c_str();  //获取html表单输入框name名为"city"的内容
    Serial.println(citycode);
  }
  else
  {
    Serial.println("error, not found citycode");
    server.send(200, "text/html", "<meta charset='UTF-8'>error, not found citycode");
    return;
  }
  //server.send(200, "text/html", "<meta charset='UTF-8'>SSID：" + ssid + "<br />password: " + password + "<br />已取得WiFi信息,正在尝试连接,请手动关闭此页面。"); //返回保存成功页面
  server.send(200, "text/html", "<meta charset='UTF-8'>已取得WiFi信息,正在尝试连接,请手动关闭此页面。"); //返回保存成功页面

  //delay(2000);
  //**************连接成功后写入wifi信息**********
  if (needsave == 1) {
    removeConfig(); // 不管有没有配置先删除一次再说。
    String payload; // 拼接构造一段字符串形式的json数据长{"ssid":"xxxxx","password":"xxxxxxxxxxx"}
    payload += "{\"ssid\":\"";
    payload += ssid;
    payload += "\",\"password\":\"";
    payload += password;
    payload += "\",\"citycode\":\"";
    payload += citycode;
    payload += "\"}";
    File wifiConfig = SPIFFS.open("/config.json", "w");
    wifiConfig.println(payload);//将数据写入config.json文件中
    wifiConfig.close();
    if (SPIFFS.exists("/config.json")) {
      Serial.println("配置信息已保存");
    }
    else Serial.println("配置信息保存失败");
  }
  delay(2000);
  ESP.restart();
}


/*
   扫描附近的WiFi，为了显示在配网界面
*/
//bool scanWiFi() {
//  Serial.println("scan start");
//  Serial.println("--------->");
//  // 扫描附近WiFi
//  int n = WiFi.scanNetworks();
//  Serial.println("scan done");
//  if (n == 0) {
//    Serial.println("no networks found");
//    scanNetworksID += "<option>no networks found</option>";
//    return false;
//  } else {
//    Serial.print(n);
//    Serial.println(" networks found");
//    for (int i = 0; i < n; ++i) {
//      // Print SSID and RSSI for each network found
//      Serial.print(i + 1);
//      Serial.print(": ");
//      Serial.print(WiFi.SSID(i));
//      Serial.print(" (");
//      Serial.print(WiFi.RSSI(i));
//      Serial.print(")");
//      Serial.println((WiFi.encryptionType(i) == AUTH_OPEN) ? " " : "*");
//      scanNetworksID += "<option>" + WiFi.SSID(i) + "</option>";
//      delay(10);
//    }
//    scanNetworksID += "</datalist>";
//    return true;
//  }
//}


void initBasic() { //初始化基础
  Serial.begin(115200);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }
  //WiFi.hostname("墨水屏台历");//设置ESP8266设备名
}

void initSoftAP() { //初始化AP模式
  WiFi.mode(WIFI_AP);
  WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
  if (WiFi.softAP(AP_NAME)) {
    Serial.println("ESP8266 SoftAP 启动");
  }
  needsave = 1;
}

void initWebServer() { //初始化WebServer
  //server.on("/",handleRoot);
  //上面那行必须以下面这种格式去写否则无法强制门户
  server.on("/", HTTP_GET, handleRoot);//设置主页回调函数
  server.onNotFound(handleRoot);//设置无法响应的http请求的回调函数
  server.on("/", HTTP_POST, handleConfigWifi); //设置Post请求回调函数
  server.begin();//启动WebServer
  Serial.println("WebServer started!");
}

void initDNS() { //初始化DNS服务器
  if (dnsServer.start(DNS_PORT, "*", apIP)) { //判断将所有地址映射到esp8266的ip上是否成功
    Serial.println("start dnsserver success.");
  }
  else Serial.println("start dnsserver failed.");
}

/*
   配置配网功能
*/
void wifiConfig()
{
  initSoftAP();
  initWebServer();
  initDNS();
  //scanWiFi();
}


void removeConfig() {
  if (SPIFFS.exists("/config.json")) { // 判断有没有config.json这个文件
    if (SPIFFS.remove("/config.json")) {
      Serial.println("删除旧配置");
    } else {
      Serial.println("删除旧配置失败");
    }
  }
}

bool WIFI_init()
{
  initBasic();
  Serial.println("");
  /*---------读取数据----------------*/
  if (SPIFFS.begin()) { // 打开闪存文件系统，记得在你连接板子下载的过程中选Flash Size的时候不要选no SPIFFS,你可以选1M、2M、3M都无所谓，因为两个文件都很小
    Serial.println("闪存文件系统打开成功");
  } else {
    Serial.println("闪存文件系统打开失败");
    Serial.println("FS not formatted. Doing that now... (can last up to 30 sec).");
    SPIFFS.format();
    Serial.println("FS formatted...");
    SPIFFS.begin();
  }
  if (SPIFFS.exists("/config.json")) { // 判断有没有config.json这个文件
    Serial.println("存在配置信息，正在自动连接");
    const size_t capacity = JSON_OBJECT_SIZE(1) + JSON_OBJECT_SIZE(2) + 80; //分配一个内存空间
    DynamicJsonDocument doc(capacity);// 声明json处理对象
    File configJson = SPIFFS.open("/config.json", "r");
    deserializeJson(doc, configJson); // json数据序列化
    const char* sta_ssid = doc["ssid"];
    const char* sta_password = doc["password"];
    const char* sta_citycode = doc["citycode"];
    strcpy(ssid, sta_ssid);
    strcpy(password, sta_password);
    citycode = sta_citycode;
    configJson.close();
    /*---------读取数据完成----------------*/

  } else {
    Serial.println("不存在配置信息，正在打开web配网模式");
    wifiConfig();
    return false;
  }
  WiFi.mode(WIFI_STA);//切换为STA模式
  //WiFi.setAutoConnect(true); //设置自动连接
  WiFi.begin(ssid, password);//连接上一次连接成功的wifi
  Serial.println("");
  Serial.print("Connect to wifi");
  int count = 0;
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    count++;
    if (count > 20) { //如果10秒内没有连上，就开启Web配网 可适当调整这个时间
      Serial.println("连接失败");
      wifiConfig();
      return false;//跳出 防止无限初始化
    }
    Serial.print(".");
  }
  Serial.println("");
  if (WiFi.status() == WL_CONNECTED) { //如果连接上 就输出IP信息 防止未连接上break后会误输出
    Serial.println("WIFI Connected!");
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());//打印esp8266的IP地址
  }
  return true;
}
